package com.fpinjava.functions.listing02_02;

public interface Function<T, U> {

  U apply(T arg);

  default <V> Function<V, U> compose(Function<V, T> f) {
    //入参： V-> T
    //返回 V-> U
    return (V x) -> apply(f.apply(x));
  }

  default <V> Function<T, V> andThen(Function<U, V> f) {
    //入参： U -> V
    //出参： T->V
    return (T x) -> f.apply(apply(x));
  }

  static <T> Function<T, T> identity() {
    //返回 T-> T
    return t -> t;
  }

  static <T, U, V> Function<V, U> compose(Function<T, U> f, Function<V, T> g) {
    //入参： T->U,V->T
    //返回： V->U
    return (V x) -> f.apply(g.apply(x));
  }

  static <T, U, V> Function<T, V> andThen(Function<T, U> f, Function<U, V> g) {
    //入参： T->U,U->V
    //返回 T->V
    return (T x) -> g.apply(f.apply(x));
  }

  static <T, U, V> Function<Function<T, U>, Function<Function<U, V>, Function<T, V>>> compose() {
    //返回 （T,U）-> ((U,V),(T,V))
    return (Function<T,U> x) -> (Function<U,V> y) -> y.compose(x);
  }

  static <T, U, V> Function<Function<T, U>, Function<Function<V, T>, Function<V, U>>> andThen() {
    //返回 (T,U) -> ((V,T),(V,U))
    return (Function<T,U> x) -> (Function<V,T> y) -> y.andThen(x);
  }

  static <T, U, V> Function<Function<U, V>, Function<Function<T, U>, Function<T, V>>> higherCompose() {
    //返回 （U,V） -> ((T,U),(T,V))
    return (Function<U,V> f) -> (Function<T,U> g) -> (T x) -> f.apply(g.apply(x));
  }

  static <T, U, V> Function<Function<T, U>, Function<Function<U, V>, Function<T, V>>> higherAndThen() {
    //返回 (T,U) -> ((U,V),(T,V))
    return (Function<T,U> f) -> (Function<U,V> g) -> (T z) -> g.apply(f.apply(z));
  }
}
